package edu.mapi.aamas.renderer;

import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;

import javax.media.j3d.BoundingSphere;
import javax.media.j3d.BranchGroup;
import javax.media.j3d.GraphicsConfigTemplate3D;
import javax.media.j3d.PhysicalBody;
import javax.media.j3d.PhysicalEnvironment;
import javax.media.j3d.Transform3D;
import javax.media.j3d.TransformGroup;
import javax.media.j3d.View;
import javax.media.j3d.ViewPlatform;
import javax.vecmath.Point3d;
import javax.vecmath.Vector3d;


/**
 * @author RJPG
 * This class is used to create a view point in 3D space using 
 * spherical coordinates and setting an pivot
 * Every method ending by 3D as a close connection with Java3D
 */
public class Camera3D {
	
	/*------------vars da classe------------------------*/
	protected double rho,phi,theta,psi;  // coordenadas esfericas
	protected Vector3d pivot,pos;        // coordenadas retangulares
	protected double scale,viewangle;    // ajustes
	
	public Renderer3D associatedrender=null;
	

	public Obj3D lockobj=null;   //aux
	
	public static final int TOP=0,BACK=1,RIGHT=2,USER=3,DEFAULT=-1;
	protected short type=DEFAULT;
	
	/*------------vars do Java3D-------------------------*/
	protected int projection;  //tipo: orthonormal ou prespectiva
	public MyCanvas3D canvas;    //bitmap de render desta cam para associar a um panel...
	
	private BoundingSphere bounds;
	private ViewPlatform viewPlatform; 
	public View view;  // vista(camera)
	private PhysicalBody body;  // distancia de olhos se usar 2 monitores...
	private PhysicalEnvironment environment;
	//private Screen3D screen;//isto pode sair: usado para render Full screen

	//roll-phi-X
	//pitch-theta-Y
	//yaw-psi-Z
	//rho
	private TransformGroup Theta =new TransformGroup(); //ang vertical
	private TransformGroup Psi =new TransformGroup(); // ang horizontal
	private TransformGroup Phi =new TransformGroup(); // roll
	private TransformGroup Rho =new TransformGroup(); // dist
	private TransformGroup Pivot =new TransformGroup(); // trasl
	private TransformGroup Scale =new TransformGroup(); //escala(zoom) 
	
	public BranchGroup fullcam=new BranchGroup();
	/*------------vars para aplica��o--------------------*/
	public double step=0.25;    //andamento em x,y,z 
	public double stepscale=0.025; // aumento no zoom
	
	/**
	 * 	Default constructor 
	 *  The viewpoint is (0,0,-10) all angles are null looking to pivot(0,0,0)
	 *  The roll is null as well (North pointed Right)
	 * 	(looking up 10 meters under wather) 
	 * 	Is associated Canvas3D that's public
	 */
	public Camera3D()
	{
		//		 coordenadas esfericas
		pivot=new Vector3d(0.0,0.0,0.0);   //para onde olha
		rho=10.0;						   //distancia ao Pivot
		phi=0.0;						   // "rool" da camera
		theta=0.0;                         // angulo vertical 
		psi=0.0;						   // angulo horizontal  
		
		pos=new Vector3d(0.0,0.0,0.0);//... coordenadas rectangulares(n�o pronto ainda)
		
		Transform3D t=new Transform3D();//Matriz aux 
	
		bounds = new BoundingSphere(new Point3d(0.0,0.0,0.0), Double.MAX_VALUE);
		
		
		Theta.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
		Theta.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
		t.setIdentity();
		t.rotX(theta);
		Theta.setTransform(t);
		
		Psi.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
		Psi.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
		t.setIdentity();
		t.rotZ(psi);
		Psi.setTransform(t);
		
		Phi.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
		Phi.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
		t.setIdentity();
		t.rotZ(phi);
		Phi.setTransform(t);
		
		Rho.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
		Rho.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
		t.setIdentity();
		t.set(new Vector3d(0.0, 0.0, rho));
		Rho.setTransform(t);

		Pivot.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
		Pivot.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
		t.setIdentity();
		t.set(pivot);
		Pivot.setTransform(t);
		
		Pivot.addChild(Psi);
		Psi.addChild(Theta);
		Theta.addChild(Phi);
		Phi.addChild(Rho);
		
		Scale=new TransformGroup();
		Scale.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
		Scale.setCapability( TransformGroup.ALLOW_TRANSFORM_READ);
		
		//		Retirar a configura��o do ecra
	    GraphicsConfigTemplate3D tmpl = new GraphicsConfigTemplate3D();
	    GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment();
	    GraphicsDevice device = env.getDefaultScreenDevice();
	    GraphicsConfiguration config = device.getBestConfiguration(tmpl);
	    
	    
	    canvas=new MyCanvas3D(config,this);
	    canvas.stopRenderer();
	    //canvas.setBackground(new Color(2, 113, 171)); 
	    canvas.setDoubleBufferEnable(true);
		
		view=new View();
		viewPlatform = new ViewPlatform();
		viewPlatform.setActivationRadius(Float.MAX_VALUE);
		viewPlatform.setBounds(bounds);
		Scale.addChild(viewPlatform);
		Rho.addChild(Scale);//<<<<<<<<<<<<<---------------------------
		view = new View();
		view.setProjectionPolicy(View.PERSPECTIVE_PROJECTION);
		projection=View.PERSPECTIVE_PROJECTION;
		view.addCanvas3D(canvas);
		view.setFrontClipDistance(0.1);
		view.setBackClipDistance(1000);
		body = new PhysicalBody();
		view.setPhysicalBody(body);
		environment = new PhysicalEnvironment();
		view.setPhysicalEnvironment(environment);
		view.attachViewPlatform(viewPlatform);
		view.setWindowResizePolicy(View.PHYSICAL_WORLD);
		
		
		// aqui � que entram as tranforma��es 
		//t.lookAt(new Point3d(0.0f,0.0f,-20.0f),new Point3d(0.0f,0.0f,0.0f),new Vector3d(1.0f,0.0f,0.0f));
		//t.invert();
		//canvas.getPixelLocationInImagePlate()
   	
		fullcam.addChild(Pivot);
		fullcam.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
		fullcam.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
				
		
	}
	
	/**
	 * Constructor
	 * @param style Type of camera 0-TOP 1-RIGHT 2-FRONT 3-USER
	 */
	public Camera3D(int style)
	{
		this();
		switch (style) { 
			case TOP:{ 
				resetTop();
				break;
			}
			case RIGHT: {
				resetRight();
				break;
			}
			case BACK:{
				resetBack();
				break;
			}
			case USER:{
				resetUser();
				break;
			}
			default:{
	    	
			}
		}
	}
	
	/**
	 * @param style Type of camera 0-TOP 1-RIGHT 2-FRONT 3-USER
	 */
	public void setType(int style)
	{
		switch (style) { 
			case TOP:{ 
				resetTop();
				break;
			}
			case RIGHT: {
				resetRight();
				break;
			}
			case BACK:{
				resetBack();
				break;
			}
			case USER:{
				resetUser();
				break;
			}
			default:{
	    	
			}
		}
	}
	/**
	 * @return the type of camera as String
	 */
	public String getStrType()
	{
	switch (type) { 
	case TOP:{ 
		return "Top";
		
	}
	case RIGHT: {
		return "Right";
		
	}
	case BACK:{
		return "Back";
		
	}
	case USER:{
		return "User";
		
	}
	default:{
		return "Custom";
	}
}
	}
	
	/**
	 * reset the camera to top view (ortho)
	 *
	 */
	public void resetTop()
	{
		setPhi(0.0);
		setPsi(0);
		setScale(0.2);
		setTheta(0);
		setRho(500.0);
		setPivot(new Vector3d(0.0,0.0,0.0));
		setProjection(View.PARALLEL_PROJECTION);
		type=TOP;
	}
	
	/**
	 * reset the camera to back view (ortho)
	 *
	 */
	public void resetBack()
	{
		setPhi(0.f);
		setPsi(0.f);
		setTheta(0.0);
		setScale(0.2);
		setTheta(Math.PI/2);
		setRho(500.0);
		setPivot(new Vector3d(0.0,0.0,0.0));
		setProjection(View.PARALLEL_PROJECTION);
		//setProjection(View.PERSPECTIVE_PROJECTION);
		type=BACK;
	}
	
	/**
	 * reset the camera to Right view (ortho)
	 *
	 */
	public void resetRight()
	{
		setPhi(0);
		setPsi(Math.PI/2);
		setTheta(Math.PI/2);
		setScale(0.2);
		setRho(500.0);
		setPivot(new Vector3d(0.0,0.0,0.0));
		setProjection(View.PARALLEL_PROJECTION);
		//setProjection(View.PERSPECTIVE_PROJECTION);
		type=RIGHT;
	}
	
	/**
	 * reset the camera to "User" view (Prespctive)
	 *
	 */
	public void resetUser()
	{
		//setPhi(Math.PI);
		setPsi(Math.PI-Math.PI/4);
		//setPsi(-Math.PI/2);
		setTheta(Math.PI/4);
		//setScale(1.0);
		setScale(0.13333333333333353);
		setRho(30.0);
		setPivot(new Vector3d(0.0,0.0,0.0));
		setProjection(View.PERSPECTIVE_PROJECTION);
		type=USER;
	}
	
	/**
	 * all angles and pivot setted null
	 * distance of view setted to 30 meters
	 */
	public void reset()
	{
		setPhi(0.0);
		setPsi(0.0);
		setTheta(0.0);
		setScale(0.2);
		setRho(30.0);
		setPivot(new Vector3d(0.0,0.0,0.0));
		setProjection(View.PARALLEL_PROJECTION);
		//setProjection(View.PERSPECTIVE_PROJECTION);
		type=DEFAULT;
	}
	
	/**
	 * @param t Vertical angle 
	 */
	public void setTheta(double t)
	{
		Transform3D tt=new Transform3D();
		theta=t;
		tt.rotX(theta);
		Theta.setTransform(tt);
	}
	
	/**
	 * @param p Roll of the camera
	 */
	public void setPhi(double p)
	{
		Transform3D t=new Transform3D();
		phi=p;
		t.rotZ(phi);
		Phi.setTransform(t);
	}
	
	/**
	 * @param p horizontal angle
	 */
	public void setPsi(double p)
	{	
		Transform3D t=new Transform3D();
		t.setIdentity();
		psi=p;
		t.rotZ(psi);
		Psi.setTransform(t);
	}
	
	/**
   	 * @param r Distance from Pivot
	 */
	public void setRho(double r)
	{	
		Transform3D t=new Transform3D();
		t.setIdentity();
		rho=r;
		t.set(new Vector3d(0.0, 0.0, rho));
		Rho.setTransform(t);
	}
	
	/**
	 * @param p Pivot where camera is looking(pointed)
	 */
	public void setPivot(Vector3d p)
	{	Transform3D t=new Transform3D();
		pivot=p;
		t.set(pivot);
		Pivot.setTransform(t);
	}
	
	/**
	 * @param s scale of universo
	 */
	public void setScale(double s)
	{	
		Transform3D t=new Transform3D();
		scale=s;
		t.setScale(scale);
		t.invert(); // inverte Matriz porque quem fica grande � a camera n�o os objectos
		Scale.setTransform(t);
	}
	
	/**
	 * @param proj View.PERSPECTIVE_PROJECTION or View.PARALLEL_PROJECTION  
	 */
	public void setProjection(int proj)
	{
		view.setProjectionPolicy(proj);	
		projection=proj;
	}
	
	/**
	 * @return the full node to be added in Java3D scene graph
	 */
	public BranchGroup getCamera3D()
	{
		return fullcam;
	}
}
